package utils

import (
	"fmt"
	"math"
	"sort"
	"strings"
	"time"
	"unicode"

	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/mozillazg/go-pinyin"
	"github.com/shopspring/decimal"
)

// ArrayColumn 泛型去重版：提取切片中结构体的某个字段值并去重
func ArrayColumn[T any, R comparable](items []T, extractor func(item T) R) []R {
	uniqueMap := make(map[R]struct{}) // 用于记录已出现的值
	var result []R
	for _, item := range items {
		value := extractor(item)
		if _, exists := uniqueMap[value]; !exists {
			uniqueMap[value] = struct{}{}
			result = append(result, value)
		}
	}
	return result
}

// arrayDiff 针对 []*gvar.Var 实现 array_diff 功能
func ArrayDiff(slice1 []*gvar.Var, slices ...[]*gvar.Var) []*gvar.Var {
	// 构建 map 用于存储其他切片的值
	elementMap := make(map[string]bool)

	// 将所有其他切片的值放入 map
	for _, slice := range slices {
		for _, val := range slice {
			elementMap[gconv.String(val)] = true
		}
	}

	// 遍历第一个切片，过滤掉 map 中存在的值
	var result []*gvar.Var
	for _, val := range slice1 {
		if !elementMap[gconv.String(val)] {
			result = append(result, val)
		}
	}

	return result
}

// Implode 泛型版本，支持任意类型切片
func Implode[T any](slice []T, delimiter string) string {
	var strSlice []string
	for _, v := range slice {
		strSlice = append(strSlice, fmt.Sprint(v))
	}
	return strings.Join(strSlice, delimiter)
}

// InArray 翻译PHP方法 in_array
func InArray[T comparable](needle T, haystack []T) bool {
	if len(haystack) <= 8 {
		for _, value := range haystack {
			if needle == value {
				return true
			}
		}
		return false
	}

	cache := make(map[T]bool, len(haystack))
	for _, value := range haystack {
		if _, ok := cache[value]; !ok {
			cache[value] = true
		}
	}
	return cache[needle]
}

// CheckOverlap 检查两个日期范围是否有重叠
func CheckOverlap(start1, end1, start2, end2 string) (bool, error) {
	// 定义时间格式
	const layout = "2006-01-02 15:04:05"

	// 解析日期字符串
	startTime1, err := time.Parse(layout, start1)
	if err != nil {
		return false, fmt.Errorf("解析 start1 日期失败: %v", err)
	}
	endTime1, err := time.Parse(layout, end1)
	if err != nil {
		return false, fmt.Errorf("解析 end1 日期失败: %v", err)
	}
	startTime2, err := time.Parse(layout, start2)
	if err != nil {
		return false, fmt.Errorf("解析 start2 日期失败: %v", err)
	}
	endTime2, err := time.Parse(layout, end2)
	if err != nil {
		return false, fmt.Errorf("解析 end2 日期失败: %v", err)
	}

	// 检查时间范围是否有效
	if startTime1.After(endTime1) || startTime2.After(endTime2) {
		return false, fmt.Errorf("时间范围无效")
	}

	// 判断是否有重叠（修改为 <= 判断）
	return !startTime1.After(endTime2) && !startTime2.After(endTime1), nil
}

func FloatAdd(a float64, b float64) decimal.Decimal {
	// 将元乘以 100，并将结果转换为 int64
	return decimal.NewFromFloat(a).Add(decimal.NewFromFloat(b))
}

func FloatSub(a float64, b float64) decimal.Decimal {
	// 将元乘以 100，并将结果转换为 int64
	return decimal.NewFromFloat(a).Sub(decimal.NewFromFloat(b))
}

func YuanToFen(amount string) int64 {
	// 将元乘以 100，并将结果转换为 int64
	dAmount, err := decimal.NewFromString(amount)
	if err != nil {
		return 0
	}

	return dAmount.Mul(decimal.NewFromInt(100)).IntPart()
}

func FenToYuan(amountInFen string) string {
	dAmount, err := decimal.NewFromString(amountInFen)
	if err != nil {
		return ""
	}

	// 将分转换为 decimal 类型的元
	return dAmount.Div(decimal.NewFromInt(100)).String() // 除以100表示分转元
}

// 获取名字中姓的首字母（支持中文和英文）
func GetFirstLetterFromName(name string) string {
	if len(name) == 0 {
		return ""
	}

	// 检查第一个字符是中文还是英文
	firstChar := []rune(name)[0]
	if unicode.Is(unicode.Han, firstChar) {
		// 处理中文名，提取姓的拼音首字母
		args := pinyin.NewArgs()
		surname := string(firstChar)
		py := pinyin.Pinyin(surname, args)
		if len(py) > 0 && len(py[0]) > 0 {
			return strings.ToUpper(string(py[0][0][0])) // 返回拼音的首字母
		}
	} else if unicode.IsLetter(firstChar) {
		// 处理英文名，直接返回第一个字母的大写形式
		return strings.ToUpper(string(firstChar))
	} else {

	}

	return ""
}

// ArrayUnique 对任意类型的切片进行去重
func ArrayUnique[T comparable](slice []T) []T {
	uniqueMap := make(map[T]struct{}) // 用于记录已出现的值
	uniqueSlice := make([]T, 0)

	for _, v := range slice {
		if _, exists := uniqueMap[v]; !exists {
			uniqueMap[v] = struct{}{}
			uniqueSlice = append(uniqueSlice, v)
		}
	}

	return uniqueSlice
}

// 检查时间段是否冲突，传入的是数组(utc时间戳)，时间相等会判断冲突
// [
// ['start_time' => 10, 'end_time' => 30],
// ['start_time' => 20, 'end_time' => 40],
// ]
type TimeInfo struct {
	StartTime int64
	EndTime   int64
}

func CheckTimeArrayConflict(timeArray []*TimeInfo) bool {
	if len(timeArray) > 1 {
		sort.Slice(timeArray, func(i, j int) bool {
			return timeArray[i].StartTime < timeArray[j].StartTime
		})
		for i, t := range timeArray {
			if i == len(timeArray)-1 {
				break
			}
			if t.EndTime > timeArray[i+1].StartTime {
				return true
			}
		}
	}

	return false
}

// ArrayReverse 泛型版，用于反转任意类型的切片
func ArrayReverse[T any](slice []T) []T {
	length := len(slice)
	reversed := make([]T, length)

	for i, v := range slice {
		reversed[length-1-i] = v
	}

	return reversed
}

// SplitAndConvert 将字符串按指定分隔符分割，并转换为指定类型的切片
func SplitAndConvert[T any](s, sep string, convertFunc func(string) (T, error)) ([]T, error) {
	parts := strings.Split(s, sep)
	result := make([]T, 0, len(parts))

	for _, part := range parts {
		trimmed := strings.TrimSpace(part)
		if trimmed == "" {
			continue // 跳过空字符串
		}
		value, err := convertFunc(trimmed)
		if err != nil {
			return nil, fmt.Errorf("转换失败: %q: %v", part, err)
		}
		result = append(result, value)
	}

	return result, nil
}

// 计算两个经纬度的距离，返回单位“米”，计算公式：
// C = sin(LatA*Pi/180)*sin(LatB*Pi/180) + cos(LatA*Pi/180)*cos(LatB*Pi/180)*cos((MLonA-MLonB)*Pi/180)
// Distance = R*Arccos(C)*Pi/180
func EarthDistance(lat1, lng1, lat2, lng2 float64) float64 {
	radius := 6378.137
	rad := math.Pi / 180.0

	lat1 = lat1 * rad
	lng1 = lng1 * rad
	lat2 = lat2 * rad
	lng2 = lng2 * rad
	theta := lng2 - lng1
	dist := math.Acos(math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta))

	return dist * radius * 1000
}

// RemoveWhitespace 移除字符串中的所有空白字符（含制表符与换行符）
func RemoveWhitespace(s string) string {
	// 按空白字符分割，再拼接
	return strings.Join(strings.Fields(s), "")
}

// ExtractLeadingNumber 提取字符串开头的数字，支持前导零
// 返回: 提取的数字(int64), 标记是否找到任何数字(bool)
func ExtractLeadingNumber(s string) (num int64, foundDigit bool) {
	foundDigit = false

	for _, r := range s {
		if unicode.IsDigit(r) {
			foundDigit = true
			digit := int64(r - '0')
			num = num*10 + digit
		} else {
			// 一旦遇到非数字字符，立即返回
			return
		}
	}

	return
}
